﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ComputerEngine;
using System.Diagnostics;

namespace Daphne.MoveHelperWrapper
{
    #region Public Wrapper Container Classes
    

    /// <summary>
    /// Engine Type Enum, for now support for internal engine and in the future CheckersBoard engines
    /// </summary>
    public enum eEngineType { Internal, CheckersBoard };

    /// <summary>
    /// Engine configuration 
    /// </summary>
    [Serializable]
    public class EngineConfig
    {
        public EngineConfig()
        {
            InternalEngineSettings = new EngineSettings();
        }

        /// <summary>
        /// Engine type
        /// </summary>
        public eEngineType EngineType
        { get; set; }

        public eCheckersType CheckersType
        { get; set; }

        /// <summary>
        /// Internal Engine Settings
        /// </summary>
        public EngineSettings InternalEngineSettings
        { get; internal set; }
    }

    /// <summary>
    /// Engine event args for progress
    /// </summary>
    public class EngineProgressEventArgs: EventArgs {
        public int Percents;
    }

    /// <summary>
    /// Board position for returning the move
    /// </summary>
    public class BoardPos
    {
        public BoardPos(int row, int col)
        {
            Row = row;
            Col = col;
        }

        public int Row
        { get; set; }

        public int Col
        { get; set; }
    }

    /// <summary>
    /// Engine Move Output
    /// </summary>
    public class EngineMoveOutput
    {
        public int CacheHit
        { get; set; }

        public int MaxDepth
        { get; set; }

        public int MaxCalmPositionDepth
        { get; set; }

        public long NumberOfNodes
        { get; set; }

        public long NodesPerSecond
        { get; set; }
    }

    
    /// <summary>
    /// Event Args for returning the move
    /// </summary>
    public class EngineFinishedEventArgs : EventArgs
    {
        public BoardPos[] Move;
        public EngineMoveOutput Output;
    }

    /// <summary>
    /// Object returned whenever a method for computing move is called
    /// </summary>
    public interface IEngineProgress
    {
        event EventHandler StartEvent;
        event EventHandler<EngineProgressEventArgs> ProgressEvent;
        event EventHandler<EngineFinishedEventArgs> FinishedEvent; 
    }

    
    /// <summary>
    /// Figure type
    /// </summary>
    public enum eFigure { WhiteMan, BlackMan, WhiteKing, BlackKing, None };

    
    /// <summary>
    /// BitBoard Container class for saving move history
    /// </summary>
    public class BitBoard
    {
        public ulong WM
        { get; set; }

        public ulong WK
        { get; set; }

        public ulong BM
        { get; set; }

        public ulong BK
        { get; set; }       
    }

    /// <summary>
    /// Container class for passing the configuration of game to the engine
    /// </summary>
    public class GameStateConfig
    {
        /// <summary>
        /// Checkers type played 
        /// TO-DO debug - not best attribute placement
        /// </summary>
        public eCheckersType CheckersType
        { get; set; }

        /// <summary>
        /// current board situation
        /// </summary>
        public eFigure[,] CheckersBoard;

        /// <summary>
        /// Side to move
        /// </summary>
        public bool WhiteToMove
        { get; set; }

        /// <summary>
        /// All the game history for draw evaluation
        /// </summary>
        public BitBoard[] GameHistory
        { get; set; }

        /// <summary>
        /// Clears given field
        /// </summary>
        public void ClearField(int row, int col)
        {
            SRemovedFigure rec = new SRemovedFigure();
            rec.Row = row;
            rec.Col = col;
            rec.figure = CheckersBoard[row, col];
            _stackRemovedPieces.Push(rec);

            CheckersBoard[row, col] = eFigure.None;         
        }

        /// <summary>
        ///  Undoes one clear
        /// </summary>
        public void UndoClear()
        {
            var rec = _stackRemovedPieces.Pop();
            CheckersBoard[rec.Row, rec.Col] = rec.figure;
        }

        /// <summary>
        /// Fully restores the board
        /// </summary>
        public void FullRestore()
        {
            while (_stackRemovedPieces.Count > 0)
            {
                UndoClear();
            }
        }

        /// <summary>
        /// Remove rec stack
        /// </summary>
        private Stack<SRemovedFigure> _stackRemovedPieces = new Stack<SRemovedFigure>();

        /// <summary>
        /// Removed figure record
        /// </summary>
        private struct SRemovedFigure
        {
            public int Row;
            public int Col;
            public eFigure figure;
        }
    }

    #endregion

    #region Engine Move Converter Helper Classes

    /// <summary>
    /// Field and figure color
    /// </summary>
    internal enum eColor { White, Black };

    /// <summary>
    /// Direction, in which figures can move
    /// </summary>
    internal enum enumDiagonalDir { NorthWest, NorthEast, SouthWest, SouthEast }
   
    /// <summary>
    /// enum figure color helper
    /// </summary>
    internal static class eFigureColorHelper
    {
        public static eColor FigureColor(this eFigure figure)
        {
            return figure == eFigure.BlackMan || figure == eFigure.BlackKing ? eColor.Black : eColor.White;
        }

    }

    /// <summary>
    /// Helper extension class for direction of move
    /// </summary>
    internal static class enumDiagonalDirExtension
    {
        public static enumDiagonalDir Opposite(this enumDiagonalDir dir)
        {
            switch (dir)
            {
                case enumDiagonalDir.NorthEast:
                    return enumDiagonalDir.SouthWest;
                case enumDiagonalDir.NorthWest:
                    return enumDiagonalDir.SouthEast;
                case enumDiagonalDir.SouthEast:
                    return enumDiagonalDir.NorthWest;
                case enumDiagonalDir.SouthWest:
                    return enumDiagonalDir.NorthEast;
                default:
                    throw new InvalidOperationException("Invalid direction.");
            }
        }
    }

    /// <summary>
    /// Helper structure for working with position at the board
    /// </summary>
    internal struct Position
    {
        public int Row;
        public int Col;


        public Position(int iRow, int iCol)
        { Row = iRow; Col = iCol; }

        /// <summary>
        /// Checks if it is empty position
        /// </summary>
        public bool IsEmpty()
        {
            return Row == -1 || Col == -1;
        }

        /// <summary>
        /// Property to get empty position
        /// </summary>
        public static Position Empty
        {
            get
            {
                return new Position(-1, -1);
            }
        }

        /// <summary>
        /// properties for getting new direction of the field
        /// </summary>
        public Position NorthWest
        {
            get { return new Position(Row + 1, Col - 1); }
        }

        public Position NorthEast
        {
            get { return new Position(Row + 1, Col + 1); }
        }

        public Position SouthWest
        {
            get { return new Position(Row - 1, Col - 1); }
        }

        public Position SouthEast
        {
            get { return new Position(Row - 1, Col + 1); }
        }

        /// <summary>
        /// Checks if the position fields are on the diagonal
        /// </summary>
        public bool IsDiagonal(Position pos)
        {
            return Math.Abs(this.Row - pos.Row) == Math.Abs(this.Col - pos.Col);
        }

        /// <summary>
        /// gets the direction at which player should go from THIS field to parameter field
        /// </summary>
        public enumDiagonalDir GetDiagonalDir(Position end)
        {
            Debug.Assert(IsDiagonal(end) == true);
            Debug.Assert(this != end);
            if (this.Row < end.Row)
            {
                if (this.Col < end.Col)
                    return enumDiagonalDir.NorthEast;
                else return enumDiagonalDir.NorthWest;
            }
            else
            {
                if (this.Col < end.Col)
                    return enumDiagonalDir.SouthEast;
                else return enumDiagonalDir.SouthWest;
            }
        }

        /// <summary>
        /// Gets next field according to the enumDiagonalDir enum class
        /// </summary>
        public Position GetNextField(enumDiagonalDir dir)
        {
            switch (dir)
            {
                case enumDiagonalDir.NorthWest:
                    return this.NorthWest;
                case enumDiagonalDir.NorthEast:
                    return this.NorthEast;
                case enumDiagonalDir.SouthWest:
                    return this.SouthWest;
                case enumDiagonalDir.SouthEast:
                    return this.SouthEast;
                default:
                    throw new InvalidOperationException("Unknown diagonal.");
            }
        }

        /// <summary>
        /// Returns field color on the checkers board
        /// </summary>
        public eColor GetFieldColor()
        {
            return (Row + Col) % 2 == 0 ? eColor.Black : eColor.White;
        }


        public static bool operator ==(Position pos1, Position pos2)
        {
            return pos1.Row == pos2.Row && pos1.Col == pos2.Col;
        }

        public static bool operator !=(Position pos1, Position pos2)
        {
            return (pos1.Row == pos2.Row && pos1.Col == pos2.Col) ? false : true;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }
    }


    /// <summary>
    /// Container class for saving engine settings
    /// </summary>
    [Serializable]
    public class CBEngineSettings
    {
        public string EnginePath
        { get; set; }

        public bool IsLoaded
        { get; set; }
    }

    #endregion


}
